/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlForeignKey;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.AddRelationMTM;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxICell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * UI for the <i>addition of many-to-many relationship</i> dialog
 * 
 * @author Tudor Dobrila
 * 
 */
public class AddRelationMTMDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 3329361709266451288L;

	private final static Logger LOGGER = Logger
			.getLogger("com.dta.idbi.patterns.ui.AddRelationMTMDialog");

	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length != 2) {
			return false;
		}

		if (!(objects[0] instanceof ISqlTable) || !(objects[1] instanceof ISqlTable)) {
			return false;
		}

		return true;
	}

	/**
	 * Addition of one-to-many relationship pattern dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public AddRelationMTMDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);
		
		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}
		
		updateMacro(element[0].getValue() + "_" + element[1].getValue());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.AbstractTransDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		AddSubsetDialog subDlg = new AddSubsetDialog(parentFrame, schema, element);
		subDlg.addOriginalTables(graph);
		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.AbstractTransDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new EditableGraph();
		AddSubsetDialog subDlg = new AddSubsetDialog(parentFrame, schema, element);
		ISqlTable[] tables = subDlg.addOriginalTables(graph);

		graph.getModel().beginUpdate();
		try {
			String tableName = element[0].getValue() + "_" + element[1].getValue();
			SqlTableCell newTable = new SqlTableCell(null, null, "", tableName, new mxGeometry(),
					"SEL_TABLE");
			addPkColumns(tables[0], newTable);
			addPkColumns(tables[1], newTable);
			newTable.relayout(graph);

			ISqlForeignKey fk1 = new SqlForeignKeyCell(null, null, "", "", newTable, tables[0],
					"FK_EDGE");
			graph.addCell(fk1, graph.getDefaultParent());

			ISqlForeignKey fk2 = new SqlForeignKeyCell(null, null, "", "", newTable, tables[1],
					"FK_EDGE");
			graph.addCell(fk2, graph.getDefaultParent());
		} finally {
			mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		final mxGraphComponent gComp = new mxGraphComponent(graph);
		gComp.addListener(mxEvent.LABEL_CHANGED, new mxIEventListener() {
			
			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				mxICell cell = (mxICell) arg1.getProperty("cell");
				updateMacro(cell.getValue().toString());
			}
		});
		gComp.setEnterStopsCellEditing(true);

		return gComp;
	}

	/**
	 * Add the primary key columns from the source table to the target table
	 * 
	 * @param sourceTable
	 * @param targetTable
	 */
	private void addPkColumns(ISqlTable sourceTable, ISqlTable targetTable) {
		List<ISqlColumn> cols = sourceTable.getColumns();
		for (ISqlColumn c : cols) {
			if (c.isPK()) {
				ISqlColumn newCol;
				try {
					newCol = (ISqlColumn) c.clone();
					newCol.setValue(sourceTable.getValue() + "_" + newCol.getValue());
					targetTable.addColumn(newCol);
				} catch (CloneNotSupportedException e) {
					LOGGER.log(Level.SEVERE, "Fatal error", e);
				}
			}
		}
	}

	/**
	 * Update the macro command
	 * 
	 * @param newTable
	 *            Name of the new table introduced to represent the relationship
	 */
	private void updateMacro(String newTable) {
		StringBuilder cmd = new StringBuilder(AddRelationMTM.COMMAND);
		cmd.append(" (<<").append(element[0].getValue()).append(">>, <<");
		cmd.append(element[1].getValue()).append(">>, <<");
		cmd.append(newTable).append(">>)");
		macroField.setText(cmd.toString());
	}
}
